package simpledb.opt;

import simpledb.tx.Transaction;
import simpledb.query.*;
import simpledb.opt.TablePlanner;
import simpledb.parse.QueryData;
import simpledb.planner.QueryPlanner;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.*;

/**
 * A query planner that optimizes using a heuristic-based algorithm.
 * @author Edward Sciore
 */
public class HeuristicQueryPlanner implements QueryPlanner {
   private Collection<TablePlanner> tableplanners = new ArrayList<TablePlanner>();
   FileOutputStream fout = null;
   FileOutputStream foutree = null;
   /**
    * Creates an optimized left-deep query plan using the following
    * heuristics.
    * H1. Choose the smallest table (considering selection predicates)
    * to be first in the join order.
    * H2. Add the table to the join order which
    * results in the smallest output.
    */
   public Plan createPlan(QueryData data, Transaction tx) {

  	   try {

  		   fout = new FileOutputStream ("lastQueryLog");
  		} catch (FileNotFoundException e) {
  			e.printStackTrace();
  		}
   	   try {

  		   foutree = new FileOutputStream ("tree");
  		} catch (FileNotFoundException e) {
  			e.printStackTrace();
  		}
	   new PrintStream(fout).println ("Will do projection on the following fields: " + data.fields());
	   new PrintStream(foutree).println(data.fields());
	   new PrintStream(foutree).println(data.tables());
	   
	   new PrintStream(fout).println ("The selection predicates are: " + data.pred());
      // Step 1:  Create a TablePlanner object for each mentioned table
      for (String tblname : data.tables()) {
         TablePlanner tp = new TablePlanner(tblname, data.pred(), tx);
         tableplanners.add(tp);
      }
  		new PrintStream(fout).println ("Created TablePlanner objects for all the tables: " + data.tables());
      
      // Step 2:  Choose the lowest-size plan to begin the join order
      new PrintStream(fout).println ("Getting the lowest-size plan to begin the join order");
      Plan currentplan = getLowestSelectPlan();
      
      
      // Step 3:  Repeatedly add a plan to the join order
      new PrintStream(fout).println ("The table join order is as follows:");
      while (!tableplanners.isEmpty()) {
         Plan p = getLowestJoinPlan(currentplan);
         if (p != null)
            currentplan = p;
         else  // no applicable join
            currentplan = getLowestProductPlan(currentplan);
      }
      
      // Step 4.  Project on the field names and return
	    try {
	        BufferedReader input =  new BufferedReader(new FileReader("lastQueryLogSelect"));
	        try {
	          String line = null;
	          while ((line = input.readLine()) != null)
	          {
	        	  new PrintStream(fout).println (line);
	          }
	        }
	        finally {
	          input.close();
	        }
	      }
	      catch (IOException ex){
	        ex.printStackTrace();
	      }
		    try {
		        BufferedReader input =  new BufferedReader(new FileReader("lastQueryLogJoin"));
		        try {
		          String line = null;
		          while ((line = input.readLine()) != null)
		          {
		        	  new PrintStream(fout).println (line);
		          }
		        }
		        finally {
		          input.close();
		        }
		      }
		      catch (IOException ex){
		        ex.printStackTrace();
		      }
      return new ProjectPlan(currentplan, data.fields());
   }
   
   private Plan getLowestSelectPlan() {
	   System.out.println("GET LOWEST");
      TablePlanner besttp = null;
      Plan bestplan = null;
      for (TablePlanner tp : tableplanners) {
         Plan plan = tp.makeSelectPlan();
         if (bestplan == null || plan.recordsOutput() < bestplan.recordsOutput()) {
            besttp = tp;
            bestplan = plan;
         }
      }
      tableplanners.remove(besttp);
      new PrintStream(fout).println ("The lowest-size plan is: " + besttp.getPlan().getTi().fileName().replaceAll(".tbl", ""));
      return bestplan;
   }
   
   private Plan getLowestJoinPlan(Plan current) {
	   System.out.println("getting lowest join blan");
      TablePlanner besttp = null;
      Plan bestplan = null;
      for (TablePlanner tp : tableplanners) {
         Plan plan = tp.makeJoinPlan(current);
         if (plan != null && (bestplan == null || plan.recordsOutput() < bestplan.recordsOutput())) {
            besttp = tp;
            bestplan = plan;
         }
      }
      new PrintStream(fout).println (besttp.getPlan().getTi().fileName().replaceAll(".tbl", ""));
      if (bestplan != null)
         tableplanners.remove(besttp);
      
      return bestplan;
   }
   
   private Plan getLowestProductPlan(Plan current) {
      TablePlanner besttp = null;
      Plan bestplan = null;
      for (TablePlanner tp : tableplanners) {
         Plan plan = tp.makeProductPlan(current);
         if (bestplan == null || plan.recordsOutput() < bestplan.recordsOutput()) {
            besttp = tp;
            bestplan = plan;
         }
      }
      tableplanners.remove(besttp);
      return bestplan;
   }
}
